/**
 * Panel dam nhiem viec play phan core cua game
 */
package view;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.RenderingHints;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import javax.swing.ImageIcon;
import javax.swing.JPanel;
import javax.swing.Timer;

import model.Constant;
import model.entity.Arrows;
import model.entity.CellPies;
import model.entity.Pieces;
import model.entity.Playing;
import model.entity.Receivers;


/**
 * @author heroandtn3
 * 
 */
public class PlayPanel extends JPanel implements ActionListener {

	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;
	public static final String KEY = "PlayPanel";
	private final int PANEL_WIDTH = Constant.PANEL_WIDTH;
	private final int PANEL_HEIGHT = Constant.PANEL_HEIGHT;
	private final int ROWS = Constant.ROWS;
	private final int COLS = Constant.COLS;
	private final int PADDING = Constant.PADDING;
	private final int BOX_SIZE = Constant.BOX_SIZE;
	private final int CELL_SIZE = BOX_SIZE + 2 * PADDING;
	private final int DRAWPANEL_WIDTH = CELL_SIZE * COLS;
	private final int DRAWPANEL_HEIGHT = CELL_SIZE * ROWS;
	private final int DRAWPANEL_X = (PANEL_WIDTH - DRAWPANEL_WIDTH) / 2;
	private final int DRAWPANEL_Y = (PANEL_HEIGHT - DRAWPANEL_HEIGHT) / 2;

	private MainPanel mainPanel;

	// thuoc tinh
	private DrawPanel drawPanel;
	private LevelDlg lvDlg;
	private WinLostDlg winlostDlg;

	private Timer timer;
	private Playing playing;
	private int[][] matrix;
	private CellPies[][] cellMatrix;
	private List<Pieces> pieces;
	private List<Receivers> receivers;
	private List<Arrows> arrows;
	private int score = 30;
	private int live = 5;
	private boolean active = false; // game da bat dau choi hay chua
	private boolean started = false; // thread da start hay chua
	private boolean pause = false; // tam dung game hay khong
	private MyLabelButton lblSettings, lblStart; // 2 nut
	private Thread pieThreadLoading;

	/**
	 * 
	 */
	public PlayPanel(MainPanel mainPanel) {
		// TODO Auto-generated constructor stub
		this.mainPanel = mainPanel;
		setLayout(null);
		this.setBackground(new java.awt.Color(0,138,0));
		this.setPreferredSize(new java.awt.Dimension(900, 680));
		initGUI();
	}
	
	private void initGUI() {

		drawPanel = new DrawPanel();
		drawPanel.setPreferredSize(new Dimension(DRAWPANEL_WIDTH, DRAWPANEL_HEIGHT));
		drawPanel.setBounds(DRAWPANEL_X, DRAWPANEL_Y, 
							DRAWPANEL_WIDTH, DRAWPANEL_HEIGHT);
		add(drawPanel);

		// menu
		lblSettings = new MyLabelButton(Constant.BUTTON_DIR + "setting", "Pause, setting game",  false);
		lblSettings.addMouseListener(new MouseAdapter() {
			@Override
			public void mouseClicked(MouseEvent e) {
				if (!pause)
					pauseGame(true);
					showSetting();
			}
		});
		lblSettings.setBounds(PANEL_WIDTH/2 + 48, PANEL_HEIGHT - 100, 48, 48);
		add(lblSettings);
		
		lblStart = new MyLabelButton(Constant.BUTTON_DIR + "play", "Start, play game",  false);
		lblStart.addMouseListener(new MouseAdapter() {
			@Override
			public void mouseClicked(MouseEvent e) {
				if (!started) {
					// neu game chua start thi start 2 thread
					startGame();
				}
				
				if (pause) {
					// neu game pause roi thi resume
					pauseGame(false);
				}
				
			}
		});
		lblStart.setBounds(PANEL_WIDTH/2 - 96, PANEL_HEIGHT - 100, 48, 48);
		add(lblStart);
		
		// dialog
		lvDlg = new LevelDlg(mainPanel.getMainFrame(), true);
		lvDlg.setPlayPanel(this);
		
		winlostDlg = new WinLostDlg(mainPanel.getMainFrame(), true);
		winlostDlg.setPlayPanel(this);
		
	}
	
	@SuppressWarnings("deprecation")
	public void pauseGame(boolean status) {
		// neu status == true thi pause game
		// nguoc lai thi resume game
		if (status && pause == false) {
			// tam dung
			this.pause = true;
			if (timer.isRunning())
				timer.stop();
			pieThreadLoading.suspend();
		} else if (pause == true) {
			// tiep tuc
			this.pause = false;
			timer.start();
			pieThreadLoading.resume();
		}
	}
	
	/**
	 * Start game bang cach start 2 luong la timer va pieThreadLoading
	 */
	private void startGame() {
		timer.start();
		pieThreadLoading.start();
		started = true;
	}
	
	private void checkNextLevel() {
		// kiem tra neu da het rec thi next sang level moi
		boolean next = true;
		for (Receivers rec : receivers) {
			if (rec.isActive()) {
				next = false;
				break;
			}
		}
		if (next) {
			pauseGame(true); // tam thoi dung game
			active = false; // deactive game de bat dau level moi
			if (playing.increaseLevel()) {
				// sang level moi
				lvDlg.getJlistLevel().setSelectedIndex(playing.getLevel());
				showSetting();
			} else {
				// het level, thang
				 // hien thi dialog thong bao la thang
				winlostDlg.setWin(true);
				winlostDlg.setVisible(true);
			}
		} 
	}
	
	private void checkGameOver() {
		if (live <= 0) { // neu het mang thi game over
			pauseGame(true);
			active = false;
			// hien thi dialog thong bao la thua
			winlostDlg.setWin(false);
			winlostDlg.setVisible(true);
		}
	}
	
	/**
	 * Reset game ve mac dinh nhu khi chua choi
	 * - Duoc goi khi thoat game (Back to Home)
	 */
	public void resetGame() {
		active = false;
		started = false;
		pause = false;
		score = 30;
		live = 5;
	}
	

	private void showSetting() {
		lvDlg.setVisible(true);
	}

	/**
	 * Cai dat game khi:
	 * - Bat dau choi game
	 * - Sang level moi 
	 */
	public void initGame() {
		timer = new Timer(30, this);
		matrix = playing.getMatrixByCurrentLevel();
		initCell();
		pieces = new ArrayList<Pieces>();
		pieThreadLoading = new Thread(new PieRunnable());
		active = true;
		started = false;
		drawPanel.repaint(); 
	}
	
	/**
	 * Tu ma tran (matrix) cua level tao ma tran cellMatrix dung de ma hoa cac thanh phan gom:
	 * - diem nhan hang (ma la 32)
	 * - huong mui ten (ma tu 17 den 31)
	 * - duong di (ma la 16)
	 */
	private void initCell() {
		cellMatrix = new CellPies[ROWS][COLS];
		receivers = new ArrayList<Receivers>(); // list diem nhan hang
		arrows = new ArrayList<Arrows>(); // list mui ten
		String mapType = playing.getMap().getImgDir();
		
		for (int i=0; i<ROWS; i++) {
			for (int j=0; j<COLS; j++) {
				int tmp = matrix[i][j];
				if (tmp == Constant.TYPE_SEND) {
					// la diem xuat hang
					cellMatrix[i][j] = new CellPies(Constant.TYPE_SEND);
				} else if ((tmp & 32) == 32) {
					// la diem nhan hang
					cellMatrix[i][j] = new CellPies(Constant.TYPE_REC);
					cellMatrix[i][j].setId(receivers.size());
					Receivers rec = new Receivers(i, j, mapType);
					receivers.add(rec);
				} else if (tmp > 16 && tmp <= 31) {
					// la huong mui ten
					cellMatrix[i][j] = new CellPies(Constant.TYPE_ARROW);
					cellMatrix[i][j].setId(arrows.size());
					Arrows arr = new Arrows(i, j, tmp, mapType);
					arrows.add(arr);
				} else if (tmp == 16) {
					// la duong di
					cellMatrix[i][j] = new CellPies(Constant.TYPE_ROAD);
				} else if (tmp == 0) {
					// khong la gi
					cellMatrix[i][j] = new CellPies(Constant.TYPE_NONE);
				}
			}
		}
	}

	/**
	 * Thuc hien viec di chuyen cac kien hang
	 * Phat hien cac mui ten va diem nhan hang roi xu ly
	 */
	private void movePiece() {
		Pieces pie;
		for (Iterator<Pieces> itr = pieces.iterator(); itr.hasNext();) {
			pie = itr.next();
			if ((pie.getX() % CELL_SIZE < pie.getSpeed()) && 
					(pie.getY() % CELL_SIZE < pie.getSpeed())) {
				int row = pie.getY() / CELL_SIZE;
				int col = pie.getX() / CELL_SIZE;
				if (row >= 0 && row < ROWS && col >= 0 && col < COLS) {
					
					int type = cellMatrix[row][col].getType();
					
					if (type == Constant.TYPE_ARROW) {
						// neu gap mui ten
						int typeArrow = arrows.get(cellMatrix[row][col].getId()).getType();
						pie.redirected(typeArrow);

					} else if ((type == Constant.TYPE_SEND)) {
						pie.redirected(4);
					} else 	if (type == Constant.TYPE_NONE) {
						// gap tuong thi quay tro lai
						int currentToX = pie.getToX();
						int currentToY = pie.getToY();
						pie.setToX(-currentToX);
						pie.setToY(-currentToY);
					} else 	if (type == Constant.TYPE_REC) {
						// gap diem nhan hang
						Receivers rec = receivers.get(cellMatrix[row][col].getId());
						if (rec.isActive() && pie.getType() == rec.getType()) {
							//an diem
							score += Constant.SCORE_EARN;
							if (live < 5) live++;
							drawPanel.setScore(Constant.SCORE_EARN);
						} else {
							// mat diem
							score -= Constant.SCORE_LOST;
							live--; // tru mang
							drawPanel.setScore(- Constant.SCORE_LOST);
						}
						drawPanel.setX(pie.getX());
						drawPanel.setY(pie.getY());
						drawPanel.setDrawing(true);
						
						itr.remove();
						
						receivers.get(cellMatrix[row][col].getId()).setActive(false);
						
						continue;
					}

				}
			}
			pie.move();
		}

	}

	/**
	 * @return the playing
	 */
	public Playing getPlaying() {
		return playing;
	}

	/**
	 * @param playing
	 *            the playing to set
	 */
	public void setPlaying(Playing playing) {
		this.playing = playing;
	}

	/**
	 * @return the lvDlg
	 */
	public LevelDlg getLvDlg() {
		return lvDlg;
	}

	/**
	 * @return the drawPanel
	 */
	public DrawPanel getDrawPanel() {
		return drawPanel;
	}

	/**
	 * @return the mainPanel
	 */
	public MainPanel getMainPanel() {
		return mainPanel;
	}

	/**
	 * @return the active
	 */
	public boolean isActive() {
		return active;
	}

	/**
	 * @param active the active to set
	 */
	public void setActive(boolean active) {
		this.active = active;
	}

	/**
	 * @return the score
	 */
	public int getScore() {
		return score;
	}

	@Override
	public void actionPerformed(ActionEvent e) {
		// TODO Auto-generated method stub
		checkGameOver();
		checkNextLevel();
		movePiece();
		
		// repaint
		for (Pieces pie : pieces) {
			drawPanel.repaint(pie.getSaveX(), pie.getSaveY(), CELL_SIZE, CELL_SIZE); 
			drawPanel.repaint(pie.getX(), pie.getY(), CELL_SIZE, CELL_SIZE); 
		}
		
		for (Receivers rec : receivers) {
			drawPanel.repaint(rec.getCol() * CELL_SIZE, rec.getRow() * CELL_SIZE,
						CELL_SIZE, CELL_SIZE*2);
		}
		
		for (Arrows arr : arrows) {
			drawPanel.repaint(arr.getCol() * CELL_SIZE, arr.getRow() * CELL_SIZE,
					CELL_SIZE, CELL_SIZE);
		}
		
		drawPanel.repaint(0, 0, DRAWPANEL_WIDTH, CELL_SIZE);
		//drawPanel.repaint();
	}

	public class DrawPanel extends JPanel implements MouseListener {
		/**
		 * 
		 */
		private static final long serialVersionUID = 1L;
		private boolean drawing = false;
		private int scoreEarn;
		private int x, y, saveY;
		private Color bgColor = new java.awt.Color(0,51,51);
		private Image imgHeart = new ImageIcon(Constant.BG_DIR + "heart.png").getImage();

		public DrawPanel() {
			addMouseListener(this);
			
		}

		@Override
		public void paintComponent(Graphics g) {
			if (active) {
				super.paintComponent(g);
				Graphics2D g2d = (Graphics2D) g;
				RenderingHints rh = new RenderingHints(
						RenderingHints.KEY_ANTIALIASING,
						RenderingHints.VALUE_ANTIALIAS_ON);
				rh.put(RenderingHints.KEY_RENDERING,
						RenderingHints.VALUE_RENDER_QUALITY);
				g2d.setRenderingHints(rh);
	
				paintMaze(g2d);
				paintArrow(g2d);
				paintPieces(g2d);
				paintReceivers(g2d);
				paintScore(g2d);
				paintInfo(g2d);
			}
		}

		private void paintMaze(Graphics2D g2d) {
			for (int row = 0; row < ROWS; row++) {
				for (int col = 0; col < COLS; col++) {
					int tmp = cellMatrix[row][col].getType();
					// ve duong di
					if (tmp == Constant.TYPE_ROAD) {
						// duong di
						g2d.setColor(Color.WHITE);
						g2d.fill3DRect(PADDING + col * CELL_SIZE, PADDING + row * CELL_SIZE,
								BOX_SIZE, BOX_SIZE, 
								true);
						
					} else if (tmp == Constant.TYPE_NONE) {
						// khong phai duong di
						g2d.setColor(bgColor);
						g2d.fill3DRect(PADDING + col * CELL_SIZE, PADDING + row * CELL_SIZE, 
								BOX_SIZE, BOX_SIZE, 
								true);
					} else if ((tmp & 64) == 64) {
						g2d.setColor(Color.WHITE);
						g2d.fill3DRect(PADDING + col * CELL_SIZE, PADDING + row * CELL_SIZE,
								BOX_SIZE, BOX_SIZE, 
								true);
						g2d.drawImage(playing.getMap().getImgSend(), 
										PADDING + col * CELL_SIZE, PADDING + row * CELL_SIZE, null);
					}
				}
			}
		}
		
		private void paintInfo(Graphics2D g2d) {
			// paint level
			g2d.setFont(new java.awt.Font("TimesRoman", Font.BOLD, 13));
			g2d.drawString("Level: " + playing.getLevel(), 10, 20);
			//paint score
			g2d.drawString("Score: " + score, 90, 20);
			// paint live
			g2d.drawString("Live: ", 480, 20);
			for (int i=1; i<=live; i++) {
				g2d.drawImage(imgHeart, 500 + 20 * i, 10, null);
			}
		}
		
		private void paintArrow(Graphics2D g2d) {
			// ve cac mui ten
			for (Arrows arr : arrows) {
				arr.paint(g2d);
			}
		}

		private void paintPieces(Graphics2D g2d) {
			// ve cac kien hang
			for (Pieces pie : pieces) {
				pie.paint(g2d);
			}
			
		}
		
		private void paintReceivers(Graphics2D g2d) {
			for (Receivers rec : receivers) {
				if (rec.isActive())
					rec.paint(g2d);
			}
		}
		
		private void paintScore(Graphics2D g2d) {
			if (drawing) {
				if (scoreEarn > 0) {
					g2d.setColor(Color.GREEN);
					g2d.drawString("+ " + scoreEarn, x, y);
				} else {
					g2d.setColor(Color.RED);
					g2d.drawString("- " + (-scoreEarn), x, y);
				}
				y --;
				if (saveY - y > 20) {
					drawing = false;
				}
			}
		}

		@Override
		public void mouseClicked(MouseEvent e) {
			// TODO Auto-generated method stub
			if (started && active  && !pause) {
				int x = e.getX();
				int y = e.getY();
				for (int row = 0; row < ROWS; row++) {
					for (int col = 0; col < COLS; col++) {
						
						if ((x > col * CELL_SIZE) && 
							(x < (col + 1) * CELL_SIZE) && 
							(y > row * CELL_SIZE) && 
							(y < (row + 1) * CELL_SIZE)) {
							// chuyen sang mui ten tiep theo
							arrows.get(cellMatrix[row][col].getId()).nextType();

						}
					}
				}
			}
		}

		@Override
		public void mousePressed(MouseEvent e) {
			// TODO Auto-generated method stub

		}

		@Override
		public void mouseReleased(MouseEvent e) {
			// TODO Auto-generated method stub

		}

		@Override
		public void mouseEntered(MouseEvent e) {
			// TODO Auto-generated method stub

		}

		@Override
		public void mouseExited(MouseEvent e) {
			// TODO Auto-generated method stub

		}

		/**
		 * @param drawing the drawing to set
		 */
		public void setDrawing(boolean drawing) {
			this.drawing = drawing;
		}

		/**
		 * @param score the score to set
		 */
		public void setScore(int score) {
			this.scoreEarn = score;
		}

		/**
		 * @param x the x to set
		 */
		public void setX(int x) {
			this.x = x;
		}

		/**
		 * @param y the y to set
		 */
		public void setY(int y) {
			this.y = y;
			this.saveY = y;
		}
	} // ket thuc lop DrawPanel
	
	private class PieRunnable implements Runnable {
		// thuc hien viec xuat cac Pieces 
		private final Object lock = new Object();
        private volatile boolean suspend = false , stopped = false;
        final String mapType = playing.getMap().getImgDir();
		@Override
		public void run() {
			// TODO Auto-generated method stub
			
			while(!stopped) {
                while (!suspend){
                    // do work
                	Receivers rec;
    				for (Iterator<Receivers> itr = receivers.iterator(); itr.hasNext(); ) {
    					rec = itr.next();
    					if (rec.isActive()) {
    						pieces.add(new Pieces(rec.getType(), mapType));
    						try {
    							Thread.sleep(7000);
    						} catch (InterruptedException e) {
    							// TODO Auto-generated catch block
    							//e.printStackTrace();
    						}
    					}	
    				}
                }
                synchronized (lock) {
                    try {
                        lock.wait();
                    } catch (InterruptedException e) {
                        Thread.currentThread().interrupt();
                        return;
                    }
                }
            }
		}
		
		@SuppressWarnings("unused")
		public void suspend(){
            suspend = true;
        }
		
        @SuppressWarnings("unused")
		public void stop(){
            suspend = true;stopped = true;
            synchronized (lock){
                lock.notifyAll();
            }
        }

        @SuppressWarnings("unused")
		public void resume(){
            suspend = false;
            synchronized (lock){
                lock.notifyAll();
            }
        }
	} // ket thuc lop PieRunnable
}
